/*
 * @(#)HexGrid.java	1.0 Apr 27, 2008
 *
 *	The MIT License
 *
 *	Copyright (c) 2008 Malachi de AElfweald <malachid@gmail.com>
 *
 *	Permission is hereby granted, free of charge, to any person obtaining a copy
 *	of this software and associated documentation files (the "Software"), to deal
 *	in the Software without restriction, including without limitation the rights
 *	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *	copies of the Software, and to permit persons to whom the Software is
 *	furnished to do so, subject to the following conditions:
 *
 *	The above copyright notice and this permission notice shall be included in
 *	all copies or substantial portions of the Software.
 *
 *	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *	THE SOFTWARE.
 */
package org.eoti.swing.hex;

import org.eoti.awt.geom.Hexagon;
import org.eoti.awt.WebColor;
import org.eoti.swing.tile.TileManager;

import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.geom.*;
import java.util.*;

public class HexGrid
extends JComponent
implements MouseListener
{
	protected TileManager tileMgr;
	protected int cols, rows, hexSize;
	protected HashMap<Point,Hexagon> hexes;
	protected HashMap<Point,String> tiles;
	// EventListenerList is broken in JDK1.5.0b2 -- reported
	//protected EventListenerList listeners;
	protected ArrayList<HexGridListener> listeners;
	protected Rectangle2D bounds2D;
	protected boolean drawBorder;
	protected boolean textOn;

	public HexGrid(int columns, int rows, int hexSize)
	{
		super();
		this.cols = columns;
		this.rows = rows;
		this.hexSize = hexSize;

		hexes = new HashMap<Point,Hexagon>();
		tiles = new HashMap<Point,String>();
		// EventListenerList is broken in JDK1.5.0b2 -- reported
		//listeners = new EventListenerList();
		listeners = new ArrayList<HexGridListener>();
		addMouseListener(this);

		bounds2D = new Rectangle2D.Double();

		for(int col=0; col<cols; col++)
		{
			for(int row=0; row<rows; row++)
			{
				Hexagon hex = new Hexagon(0, 0, hexSize);
				if(col == 0)
				{
					if(row == 0)		// TopLeft HEX
						hex.align(bounds2D, Hexagon.Direction.NorthWest);
					else
					{
						getHex(col,row-1).attach(hex,
							(row % 2 == 0? Hexagon.Direction.SouthWest: Hexagon.Direction.SouthEast)
						);
					}
				}else{
					getHex(col-1, row).attach(hex, Hexagon.Direction.East);
				}

				bounds2D.add(hex.getBounds2D());
				hexes.put(new Point(col, row), hex);
			}
		}

		setBounds(bounds2D);
		Dimension size = new Dimension();
		size.setSize(bounds2D.getWidth(), bounds2D.getHeight());
		setPreferredSize(size);
		//setSize(size);
	}

	public void setTextOn(boolean enabled){textOn = enabled;}
	public boolean getTextOn(){return textOn;}

	public void setBounds(Rectangle2D bounds)
	{
		this.bounds2D = bounds;
		super.setBounds(bounds.getBounds());
	}
	public Rectangle2D getBounds2D(){return bounds2D;}

	public void setTileManager(TileManager tileMgr){this.tileMgr = tileMgr;}
	public TileManager getTileManager(){return tileMgr;}

	public void setBorderPainted(boolean drawBorder){this.drawBorder = drawBorder;}
	public boolean getBorderPainted(){return drawBorder;}

	public void paintComponent(Graphics g)
	{
		for(Point p : hexes.keySet())
			paintHexagon(g, p);
	}

	protected void paintHexagon(Graphics g, Point position)
	{
		Hexagon hex = hexes.get(position);
		Graphics2D g2 = (Graphics2D)g;
		Graphics2D clippedG2 = (Graphics2D)g.create();
		clippedG2.clip(hex);
		g2.setColor(getBackgroundColor(position));
		g2.fillPolygon(hex);
		if(tileMgr != null)
		{
			String tileName = tiles.get(position);
			if(tileName != null)
				tileMgr.drawTile(tileName, clippedG2, hex.getBounds(), this);
		}

		if(drawBorder)
		{
			g2.setColor(getForegroundColor(position));
			g2.drawPolygon(hex);
		}

		if(textOn)
		{
			String s = getText(position);
			Font f = new Font("SansSerif", Font.PLAIN, 12);
			FontMetrics fm = g.getFontMetrics(f);
			int swidth = fm.stringWidth(s);
			Point2D center = hex.getCenter();
			clippedG2.drawString(
				s,
				(int)(center.getX() - (swidth/2)),
				(int)center.getY()
			);
		}

		clippedG2.dispose();
	}

	protected Color getForegroundColor(Point position)
	{
		return WebColor.Black.getColor();
	}

	protected Color getBackgroundColor(Point position)
	{
		return WebColor.CornSilk.getColor();
	}

	protected String getText(Point position)
	{
		return "Hex[" + position.x + "x" + position.y + "]";
	}

	public Hexagon getHex(Point position){return hexes.get(position);}
	public Hexagon getHex(int col, int row){return getHex(new Point(col,row));}
	public String getTileName(Point position){return tiles.get(position);}
	public String getTileName(int col, int row){return getTileName(new Point(col,row));}
	public BufferedImage getTile(Point position)
	{
		if(tileMgr == null) return null;
		return tileMgr.getTile(getTileName(position));
	}
	public BufferedImage getTile(int col, int row){return getTile(new Point(col,row));}

	public void setTileName(Point position, String tileName){tiles.put(position,tileName);}
	public void setTileName(int col, int row, String tileName){setTileName(new Point(col,row),tileName);}

	public void addHexGridListener(HexGridListener listener)
	{
		// EventListenerList is broken in JDK1.5.0b2 -- reported
		//listeners.add(HexGridListener.class, listener);
		listeners.add(listener);
	}

	public void removeHexGridListener(HexGridListener listener)
	{
		// EventListenerList is broken in JDK1.5.0b2 -- reported
		//listeners.remove(HexGridListener.class, listener);
		listeners.remove(listener);
	}

	protected Point findHex(MouseEvent mouseEvent)
	{
		/**
	     * OPTIMIZE this
	     */
	    for(int col=0; col<cols; col++)
	    {
	    	for(int row=0; row<rows; row++)
	    	{
	    		Point position = new Point(col, row);
	    		Hexagon hex = getHex(position);
	    		if(hex.contains(mouseEvent.getX(), mouseEvent.getY()))
	    			return position;
	    	}
	    }

	    return null;
	}

	protected void notifyListeners(MouseEvent mouseEvent)
	{
		// EventListenerList is broken in JDK1.5.0b2 -- reported
		//if(listeners.getListenerCount(HexGridListener.class) == 0)
		if(listeners.size() == 0)
			return;

		Point position = findHex(mouseEvent);
		if(position == null) // not one of the hexes
			return;

		Hexagon hex = getHex(position);
		HexGridEvent event = new HexGridEvent(this, hex, position, mouseEvent);
		// EventListenerList is broken in JDK1.5.0b2 -- reported
		//for(HexGridListener listener : listeners.getListeners(HexGridListener.class))
		for(HexGridListener listener : listeners)
			listener.hexChosen(event);
	}

	public void mouseClicked(MouseEvent event)
	{
		notifyListeners(event);
	}

	public void mouseEntered(MouseEvent event)
	{
		notifyListeners(event);
	}

	public void mouseExited(MouseEvent event)
	{
		notifyListeners(event);
	}

	public void mousePressed(MouseEvent event)
	{
		notifyListeners(event);
	}

	public void mouseReleased(MouseEvent event)
	{
		notifyListeners(event);
	}

}